﻿using System;
using SoftwareConsulting.BI11.NumericalMethods.DhbFunctionEvaluation;

namespace SoftwareConsulting.BI11.NumericalMethods.Iterations
{
    /// <summary>
    /// Класс итеративного процесса
    /// </summary>
    public abstract class IterativeProcess
    {        
        private int _iterationsCount;//количество выполненных итераций        
        private int _maximumIterationsCount = 50;//максимально допустимое число итераций        
        private double _desiredPrecision = MathExtended.DefaultNumericalPrecision;//желаемая точность
        private double _realPrecision;//достигнутая точность

        /// <summary>
        /// Выполняет процесс
        /// </summary>
        public void Evaluate()
        {
            _iterationsCount = 0;
            CreateRandomPopulation();
            while (_iterationsCount++ < _maximumIterationsCount)
            {
                _realPrecision = DoIteration();
                if (IsDesiredPrecisionAchieved)
                    break;
            }
            FinalizeIterations();
        }

        /// <summary>
        /// Выполняет итерацию
        /// </summary>
        /// <returns></returns>
        abstract public double DoIteration();

        /// <summary>
        /// Завершает итерации
        /// </summary>
        public virtual void FinalizeIterations()
        {
        }

        /// <summary>
        /// Возвращает желаемую погрешность
        /// </summary>
        public double DesiredPrecision
        {
            get 
            { 
                return _desiredPrecision; 
            }
        }

        /// <summary>
        /// Устанавливает желаемую погрешность
        /// </summary>
        /// <param name="newPrecision"></param>
        public void SetDesiredPrecision(double newPrecision)
        {
            if (newPrecision <= 0)
                throw new ArgumentOutOfRangeException("newPrecision");
            _desiredPrecision = newPrecision;            
        }

        /// <summary>
        /// Возвращает количество выполненных итераций
        /// </summary>
        public int IterationsCount
        {
            get 
            { 
                return _iterationsCount; 
            }
        }

        /// <summary>
        /// Возвращает максимально допустимое число итераций
        /// </summary>
        public int MaximumIterationsCount
        {
            get 
            { 
                return _maximumIterationsCount; 
            }
        }

        /// <summary>
        /// Устанавливает максимально допустимое число итераций
        /// </summary>
        public void SetMaximumIterationsCount(int newMaximum)            
        {
            if (newMaximum < 1)
                throw new ArgumentOutOfRangeException("newMaximum");
            _maximumIterationsCount = newMaximum;            
        }

        /// <summary>
        /// Возвращает достигнутую погрешность
        /// </summary>
        public double RealPrecision
        {
            get 
            { 
                return _realPrecision; 
            }
        }

        /// <summary>
        /// Возвращает флаг достигнутой погрешности
        /// </summary>
        public bool IsDesiredPrecisionAchieved
        {
            get 
            { 
                return _realPrecision < _desiredPrecision; 
            }
        }

        /// <summary>
        /// Инициализирует параметры процесса
        /// </summary>
        public virtual void CreateRandomPopulation()
        {
        }

        /// <summary>
        /// Возвращает относительную ошибку
        /// </summary>
        /// <param name="epsilon"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public double GetRelativeError(double epsilon, double x)
        {
            return x > MathExtended.DefaultNumericalPrecision ? epsilon / x : epsilon;
        }
    }
}
